package tcp;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class 支持多个客户端同时处理的长连接Server {
    private static final HashMap<String,String> map=new HashMap<>();
    static {
        map.put("apple","苹果");
        map.put("banana","香蕉");
    }
    private static class ServerTask implements Runnable{//处理的是：accept往下的这一堆全部封装起来
        private final Socket socket;
        private ServerTask(Socket socket){
            this.socket=socket;
        }
        @Override
        public void run() {
            try{
                InetAddress inetAddress = socket.getInetAddress();
                Log.println("对方的IP地址是" + inetAddress);
                int port = socket.getPort();
                System.out.println("对方的端口是" + port);
                InputStream is = socket.getInputStream();//进行处理，拿到输入流，从输入流中进行读数据
                Scanner scanner = new Scanner(is, "UTF-8");
                OutputStream os = socket.getOutputStream();//os:用于写数据
                //OutputStream->OutputStreamWriter->PrintWriter
                OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
                PrintWriter printWriter = new PrintWriter(writer);


                //在同一个连接中进行不断的读请求，处理业务，发送响应
                //循环结束条件：对方不再发请求就结束了。怎么不再发了：自己设计
                //客户端client挂断电话，表示本次连接的请求响应处理结束
                //客户端挂电话怎么体现—客户端通过关闭连接的方式来体现
                //怎么知道关闭连接了—输入流读不到了，或者说is读到了EOS(-1)
                while (scanner.hasNextLine()) {//我们已经封装成Scanner了，只要hasNextline()返回false就是没有下一行了
                    //2.读取请求
                    //字节形式不好读，封装成字符形式读取
                    String header = scanner.nextLine();//"我是Java19班的\r\n",同时nextLine()会帮忙把换行符去掉
                    //TODO:像UDP一样，应该做一个请求格式检查，看读到的header是否是正确的，不是的话不应该处理请求
                    String englishWord = scanner.nextLine();
                    Log.println("英文单词" + englishWord);
                    //3.处理业务
                    //读到了开始进行业务翻译
                    String chinseWord = map.getOrDefault(englishWord, "不认识");
                    Log.println("中文单词" + chinseWord);
                    //4.发送响应
                    //好的\r\n苹果\r\n；把OutputStream进行对象的封装

                    Log.println("服务器进行发送");
                    printWriter.printf("好的\r\n%s\r\n", chinseWord);
                    printWriter.flush();//不要忘记flush刷新
                    Log.println("服务器发送成功");
                }
                socket.close();//上面无论是读还是响应都没关闭，统一调用close()连输入流和输出流都关掉了
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws IOException {
        //直接使用线程池，直接使用Java中预先定义好的线程池(Executors下的)
        ExecutorService service = Executors.newFixedThreadPool(10);//定长的
        //给线程池提交任务即可，任务是以Runnable形式体现
        //accept 主线程【等待客户端连接上来】
        //剩下的工作，交给线程中的线程去执行

        //1.开店（创建socket）
        Log.println("服务器启动，监听在TCP：9090 端口");
        ServerSocket serverSocket = new ServerSocket(9090);//UDP的8080端口绑定不影响TCP的使用，端口的唯一绑定是和协议有关系的
        //进行循环
        while(true) {
            //TCP和UDP不一样的点，TCP是面向连接的，所以需要先进行连接
            //1.接通连接（电话）——accept
            Log.println("等待客户端连接上来");
            Socket socket=serverSocket.accept();//会有阻塞
            Log.println("有新的客户端连接上来");

            //主线程就是不断接电话；接个电话交给下一个人，接个电话交给下一个人

            ServerTask task=new ServerTask(socket);//封装好让线程池处理的任务对象
            service.execute(task);
        }
    }
}
